﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "IDocument.h"
#include "LevelUtils.h"
#include "ClipBoardUtils.h"
#include "ContainerUtil.h"
#include "MathUtils.h"
#include "GbmpBuiltInCategoryUniIdentitiesNew.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace std;

double ClipBoardUtils::GetLevelElevation(const IDocument* pDoc, ElementId levelId)
{
    DBG_WARN_AND_RETURN_UNLESS(pDoc, 0.0, L"文档为空！",L"GDMPLab",L"2024-03-30");
    const ILevel* pLevel = LevelUtils::GetLevelById(pDoc, levelId);
    if (pLevel)
    {
        return pLevel->GetElevation();
    }
    else
    {
        return 0.0;
    }
}

// 获取所有非楼层标高
std::set<ElementId> ClipBoardUtils::GetAllNonStoreyLevel(const IDocument* pDoc)
{
    std::set<ElementId> nonStoreyLevel;
    std::vector<IElement*> levels = pDoc->GetElementsByCategory(BuiltInCategoryUniIdentities::BICU_NEW_ELEVATION);
    FOR_EACH(level, levels)
    {
        ElementId levelId = level->GetElementId();
        const ILevel* pLevel = LevelUtils::GetLevelById(pDoc, levelId);
        if (!pLevel->GetBuildingId().IsValid())
        {
            nonStoreyLevel.insert(levelId);
        }
    }

    return nonStoreyLevel;
}

//对所有ILevel进行排序，按照标高值由小至大
std::set<std::pair<double, ElementId>> ClipBoardUtils::GetAllLevelsFromLowToHigh(const IDocument* pDoc)
{
    std::vector<IElement*> levels = pDoc->GetElementsByCategory(BuiltInCategoryUniIdentities::BICU_NEW_ELEVATION);
    std::set<std::pair<double, ElementId>> levelSet;
    FOR_EACH(level, levels)
    {
        ElementId levelId = level->GetElementId();
        std::pair<double, ElementId> val(GetLevelElevation(pDoc, levelId), levelId);
        levelSet.insert(val);
    }

    return levelSet;
}

ElementId ClipBoardUtils::FindNearestLevelByHeight(IDocument* pDoc, double elevation, const std::set<ElementId>& excludedLevelIds)
{
    auto levelSet = GetAllLevelsFromLowToHigh(pDoc);

    for (auto it = levelSet.begin(); it != levelSet.end();)
    {
        if (excludedLevelIds.find(it->second) != excludedLevelIds.end())
            it = levelSet.erase(it);
        else
            ++it;
    }

    if (levelSet.size() == 0)
    {
        return ElementId::InvalidID;
    }

    for (auto iter = levelSet.begin(); iter != levelSet.end(); ++iter)
    {
        //最低标高
        if (!MathUtils::IsGreaterThan(elevation, iter->first, Constants::DOUBLE_EPS))
        {
            if (iter == levelSet.begin())
            {
                return iter->second;
            }

            // h0 < elevation < h1 ，则取两者中最近的
            auto iterPrevious = iter;
            --iterPrevious;
            double h0 = iterPrevious->first;
            double h1 = iter->first;

            if (MathUtils::IsGreaterThan(h1 - elevation, elevation - h0, Constants::DOUBLE_EPS) ||
                MathUtils::IsEqual(h1 - elevation, elevation - h0, Constants::DOUBLE_EPS))
            {
                return iterPrevious->second;
            }
            else if (MathUtils::IsLessThan(h1 - elevation, elevation - h0, Constants::DOUBLE_EPS))
            {
                return iter->second;
            }
        }

        // elevation > [最高标高值]  绑定至最高标高楼层
        auto iterNext = iter;
        iterNext++;
        if ((iterNext) == levelSet.end())
        {
            return iter->second;
        }
    }

    DBG_WARN(L"不可能找不到绑定楼层！",L"GDMPLab",L"2024-03-30");
    return levelSet.begin()->second;
}